using System;
using System.Collections.Generic;
using System.Text;

/* dependence: None
 * 
 * 
 * 2.3 20120721 export and import settings
 * 2.4 20120807 UtilTextRender for handling templates.
 * 
 * 
 * 
 * 
 * */
namespace wicGeneral
{
    /// <summary>
    /// handles common text and math equation operations
    /// </summary>
    public static class UtilText
    {

        /// <summary>
        /// escape characters to their safe form representation
        /// </summary>
        static Dictionary<char, string> escapeCharacters;
        static List<char> escapeCharList;
        static Dictionary<char, char> reverseMapping;

        public static void init()
        {
            escapeCharacters = new Dictionary<char, string>();

            // begin character list
            escapeCharacters['\\'] = @"\\";     //this should be at the begining to avoid over substitution
            escapeCharacters['\n'] = @"\n";
            escapeCharacters['\r'] = @"\r";
            escapeCharacters['\t'] = @"\t";
            escapeCharacters['/'] = @"\s";

            escapeCharacters['\''] = @"\p"; // apostrophe
            escapeCharacters['\"'] = @"\q"; // quotation

            escapeCharList = new List<char>();
            escapeCharList.AddRange(escapeCharacters.Keys);
            reverseMapping = new Dictionary<char, char>();

            List<string> tempMap = new List<string>(escapeCharacters.Values);
            for (int i = 0; i < tempMap.Count; i++)
                reverseMapping.Add(tempMap[i][1], escapeCharList[i]);
        }
        
        /// <summary>
        /// substitute the escape characters by corresponding characters in escapeCharacters
        /// </summary>
        /// <param name="product"></param>
        public static string toSafeString(string product)
        {
            foreach (char esc in escapeCharList)
                product = product.Replace(esc.ToString(), escapeCharacters[esc]);
            return product;
        }

        /// <summary>
        /// from safe string to normal ones
        /// </summary>
        /// <param name="product"></param>
        /// <returns></returns>
        public static string fromSafeString(string safeString)
        {
            string normalString = "";
            int posOfSlash = 0;
            int posOfCompletion = 0;

            posOfSlash = safeString.IndexOf('\\');
            while (posOfSlash != -1)
            {
                normalString += safeString.Substring(posOfCompletion, posOfSlash - posOfCompletion);
                try
                {
                    normalString += reverseMapping[safeString[posOfSlash + 1]];   //mind key not found exception
                    posOfCompletion = posOfSlash + 2;
                }
                catch (System.Collections.Generic.KeyNotFoundException)
                {
                    normalString += "?";
                    posOfCompletion = posOfSlash + 1;
                }

                posOfSlash = safeString.IndexOf('\\', posOfCompletion);
            }
            normalString += safeString.Substring(posOfCompletion);
            return normalString;
        }

        /// <summary>
        /// filter special characters to avoid Jacking and bugs
        /// deprecated
        /// </summary>
        /// <param name="beforeFilter"></param>
        /// <returns></returns>
        public static string filterKeyWords(string beforeFilter)
        {
            beforeFilter = beforeFilter.Replace("\r", "\\r");
            beforeFilter = beforeFilter.Replace("\n", "\\n");
            beforeFilter = beforeFilter.Replace("\t", "\\t");
            beforeFilter = beforeFilter.Replace("/", "//");  //for XML label
            return beforeFilter; //actually afterFilter now
        }
        /// <summary>
        /// deprecated
        /// </summary>
        /// <param name="afterFilter"></param>
        /// <returns></returns>
        public static string reverseFilterKeyWords(string afterFilter)
        {
            afterFilter = afterFilter.Replace("\\t", "\t");
            afterFilter = afterFilter.Replace("\\n", "\n");
            afterFilter = afterFilter.Replace("\\r", "\r");
            afterFilter = afterFilter.Replace("//", "/");
            return afterFilter; //actually beforeFilter now
        }

        public static int StringIndexOfReversed(string str, char target, int startPos)
        {
            for (; startPos >= 0; startPos--)
                if (str[startPos] == target)
                    break;
            return startPos;
        }

        /// <summary>
        /// return the string inside the designated brackets,
        /// else return ""
        /// </summary>
        /// <param name="source"></param>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <returns></returns>
        public static string ReadBracket(string source, string left, string right)
        {
            int Start = source.IndexOf(left);
            int End = source.IndexOf(right);

            if (Start == -1 || End == -1 || Start > End)
                return "";
            else
                return source.Substring(Start + left.Length, End - Start - left.Length);
        }
        /// <summary>
        /// read a bracket designated by {left} and {right} after {index},
        /// extract the content to {return} and point the end position in {index}
        /// </summary>
        /// <param name="source"></param>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static string ReadBracketWithIndex(string source, string left, string right, ref int index)
        {
            int Start = source.IndexOf(left, index);
            int End = source.IndexOf(right, index);

            if (Start == -1 || End == -1 || Start > End)
            {
                index = -1;
                return "";
            }
            else
            {
                index = End + right.Length;
                return source.Substring(Start + left.Length, End - Start - left.Length);
            }
        }
        /// <summary>
        /// split the sentence according to spaces and punctuations
        /// this is the first step of analysis
        /// 
        /// leaving the quotation mark surrounded parts as a whole.
        /// </summary>
        /// <param name="sentence">a complete sentence</param>
        /// <returns>separated words</returns>
        public static string[] SplitSentenceByContent(string sentence)
        {
            List<string> weStrings = new List<string>();
            string state = "normal";
            int copyStartIndex = 0;
            int searchStartIndex = 0;
            int endIndex = 0;

            do
            {
                endIndex = sentence.IndexOfAny(new char[] { ' ', '\"' }, searchStartIndex);

                if (endIndex != -1)
                {
                    if (state != "quotation" &&
                        (copyStartIndex != endIndex ||
                        copyStartIndex > 0 && sentence[copyStartIndex - 1] == ' ' && sentence[copyStartIndex] == ' '))
                        weStrings.Add(sentence.Substring(copyStartIndex, endIndex - copyStartIndex));
                }
                else
                {
                    if (copyStartIndex == 0 || copyStartIndex > 0 && sentence[copyStartIndex - 1] != '\"')
                        weStrings.Add(sentence.Substring(copyStartIndex));
                    break;
                }

                if (state == "normal")
                {
                    if (sentence[endIndex] == '\"')
                    {
                        copyStartIndex = endIndex + 1;
                        searchStartIndex = copyStartIndex;
                        state = "quotation";
                    }
                    else
                    {
                        copyStartIndex = endIndex + 1;//skip ' ' or '\r'
                        searchStartIndex = copyStartIndex;
                    }
                }
                else if (state == "quotation")
                {
                    if (sentence[endIndex] == '\"')
                    {
                        weStrings.Add(sentence.Substring(copyStartIndex, endIndex - copyStartIndex));
                        state = "normal";
                        copyStartIndex = endIndex + 1;
                        searchStartIndex = copyStartIndex;
                    }
                    else
                    {
                        searchStartIndex = endIndex + 1;
                    }
                }

            } while (searchStartIndex != -1);
            return weStrings.ToArray();
        }

        public static string Concatenate(string[] tokens, string separator)
        {
            string result = "";
            if (tokens.Length == 0)
                return result;
            int i = 0;
            for (; i < tokens.Length - 1; i++)
                result += tokens[i] + separator;

            result += tokens[i];
            return result;
        }
        public static string Concatenate(string[] tokens, string separator, int startIndex)
        {
            return Concatenate(tokens, separator, startIndex, "", "");
        }
        public static string Concatenate(string[] tokens, string separator, int startIndex, string bracketLeft, string bracketRight)
        {
            string result = "";
            int i = startIndex;
            for (; i < tokens.Length - 1; i++)
                result += bracketLeft + tokens[i] + bracketRight + separator;
            if (tokens.Length > 0)
                result += bracketLeft + tokens[i] + bracketRight;
            return result;
        }
        /// <summary>
        /// find the first occurence of anyOf
        /// </summary>
        /// <param name="source"></param>
        /// <param name="anyOf"></param>
        /// <param name="startIndex"></param>
        /// <returns>If none is found, returns -1</returns>
        public static int StringIndexOfAnyString(string source, string[] anyOf, int startIndex)
        {
            string firstLetters = "";
            foreach (string s in anyOf)
                firstLetters += s[0];
            char[] firstLettersChar = firstLetters.ToCharArray();

            startIndex = source.IndexOfAny(firstLettersChar, startIndex);

            while (startIndex != -1)
            {
                foreach (string s in anyOf)
                    if (source.Substring(startIndex).StartsWith(s))
                        return startIndex;
                startIndex = source.IndexOfAny(firstLettersChar, startIndex + 1);
            }
            return -1;
        }
        /// <summary>
        /// use stack to find paired brackets of specified type and output the content inside the brackets.
        /// if right bracket is empty, then read to end of line
        /// </summary>
        /// <param name="source">string you want to read</param>
        /// <param name="left">left bracket</param>
        /// <param name="right">right bracket</param>
        /// <returns>content in the bracket</returns>
        /// <exception>bracket not found</exception>
        public static string ReadPairedBrackets(string source, string left, string right)
        {
            int index = 0;
            return ReadPairedBrackets(ref source, ref index, left, right);
        }
        /// <summary>
        /// use stack to find paired brackets of specified type and output the content inside the brackets.
        /// If successful the index will be set to the end of the bracket+1.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="index"></param>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <returns></returns>
        public static string ReadPairedBrackets(ref string source, ref int index, string left, string right)
        {
            int leftBegin = source.IndexOf(left, index);
            int rightBegin = leftBegin + left.Length;   //avoid dead loop
            if (leftBegin == -1)
                throw new Exception("Brackets not found");
            if (right.Length == 0)
            {
                index = source.Length;
                return source.Substring(leftBegin);
            }

            char[] anyOf = { left[0], right[0] };
            int depth = 1;
            
            while (depth > 0)
            {
                rightBegin = source.IndexOfAny(anyOf, rightBegin);
                if (rightBegin == -1)
                    throw new Exception(string.Format("Brackets not paired:{0}", source.Substring(index)));
                if (source.Substring(rightBegin).StartsWith(right)) //check right first, to deal with left == right case
                {
                    depth -= 1;
                }
                else if (source.Substring(rightBegin).StartsWith(left))
                {
                    depth += 1;
                }
                if (depth > 0)
                    rightBegin++;
            }
            index = rightBegin + right.Length;
            return source.Substring(leftBegin + left.Length, rightBegin - leftBegin - left.Length);
        }

        public static string writeIntsToString(int[] weInts, string separator)
        {
            string result = "";
            int i = 0;
            for (i = 0; i < weInts.Length - 1; i++)
            {
                result += weInts[i].ToString() + separator;
            }
            if (weInts.Length > 0)
                result += weInts[i];
            return result;
        }
        public static List<int> readIntsFromString(string Items, string separator)
        {
            List<int> weInts = new List<int>();
            foreach (string item in Items.Split(separator.ToCharArray()))
            {
                if (item != "")
                    weInts.Add(int.Parse(item));
            }
            return weInts;
        }

    }
}
